package net.sansi.v3correctserver.service.camera;

import cn.hutool.extra.spring.SpringUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.sansi.v3correctserver.exception.ServiceException;
import net.sansi.v3correctserver.util.CmmUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

/**
 * Created by Fang
 * create time : 2025/2/18
 * IDE : IntelliJ IDEA
 */

@Order(100000)
@Service
@Slf4j
public class CameraTcpService implements CommandLineRunner {

    @Value("${correct.camera-server.address}")
    private String address;
    @Value("${correct.camera-server.port}")
    private Integer port;


    private long lastRequestTime = 0;
    private long lastResponseTime = 0;
    // 是否在线
    @Getter
    private static boolean isOnline = false;

    /**
     * 获取SpringbootBean 的快捷方式
     *
     * @return CameraTcpService
     */
    public static CameraTcpService getInstance() {
        return SpringUtil.getBean(CameraTcpService.class);
    }

    @Override
    public void run(String... args) {
        new Thread(() -> {
            while (true) {
                if (System.currentTimeMillis() - lastResponseTime > 10000) heart();
                CmmUtil.sleep(5000);
            }
        }).start();
    }

    /**
     * 建立一个新的 socket 连接
     *
     * @return Socket
     * @throws IOException 异常
     */
    public Socket getSocket() throws IOException {
        return new Socket(address, port);
    }

    /**
     * 请求
     *
     * @param message 信息
     * @return 响应消息
     */
    public String request(String message) {
        return request(message, 30000);
    }

    /**
     * 一次请求
     * 1. 发送请求
     * 2. 阻塞等待响应
     *
     * @param message 消息
     * @return 消息
     */
    public String request(String message, long timeout) {
        try (Socket socket = getSocket()) {
            FutureTask<String> future = new FutureTask<>(() -> {
                lastRequestTime = System.currentTimeMillis();
                log.info("发送请求: {}", message);
                write(socket, message);
                String resp = read(socket);
                lastResponseTime = System.currentTimeMillis();
                log.info("响应结果：{}", resp);
                return resp;
            });
            new Thread(future).start();
            String resp = future.get(timeout, TimeUnit.MILLISECONDS);
            if (!isOnline) {
                isOnline = true;
                log.info("相机服务恢复正常 [request] {}", message);
            }
            lastResponseTime = System.currentTimeMillis();
            return resp;
        } catch (Exception e) {
            if (isOnline) {
                isOnline = false;
                log.info("相机服务断开 [request] {}", message, e);
            }
            //log.error("请求相机失败 [request] {} [error] {}", message, e.getMessage());
            throw new ServiceException("请求相机失败 [request] {} [error] {}", message, e.getMessage());
        }
    }

    /**
     * 心跳请求
     */
    private void heart() {
        try {
            String resp = request("/PING");
        } catch (Exception ignore) {
        }
    }

    /**
     * 些入消息
     *
     * @param message 消息
     * @throws Exception 异常
     */
    private void write(Socket socket, String message) throws Exception {
        try (PrintWriter out = new PrintWriter(socket.getOutputStream(), true, StandardCharsets.UTF_8)) {
            char[] data = message.toCharArray();
            int len = data.length;
            char[] lenBuffer = int2Buffer(len);
            out.write(lenBuffer);
            out.write(data);
        }
    }

    /**
     * 读取消息
     *
     * @return 消息
     * @throws IOException 异常
     */
    private String read(Socket socket) throws IOException {
        try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(),
                StandardCharsets.UTF_8))) {
            char[] buffer = new char[4];
            in.read(buffer);
            int len = buffer2Int(buffer);
            char[] data = new char[len];
            in.read(data);
            return new String(data);
        }
    }

    /**
     * 将字节数组转成int
     *
     * @param buffer 字节
     * @return int
     */
    private int buffer2Int(char[] buffer) {
        int len = 0;
        for (int i = buffer.length - 1, j = 0; i > -1; i--, j += 8) {
            len |= (buffer[i] & 0xff) << j;
        }
        return len;
    }

    /**
     * 将int转成字节数组
     *
     * @param len int
     * @return 字节
     */
    private char[] int2Buffer(int len) {
        char[] buffer = new char[4];
        for (int i = buffer.length - 1, j = 0; i > -1; i--, j += 8) {
            buffer[i] = (char) ((len >>> j) & 0xff);
        }
        return buffer;
    }

}
